﻿using System;
using System.Collections.Generic;
using System.Drawing;
using RRRSRoguelike.Entities.Props;
using RRRSRoguelike.Enums;
using RRRSRoguelike.Helpers;
using RRRSRoguelike.Managers;
using RRRSRoguelikeData;

namespace RRRSRoguelike.Entities.Creatures
{
	/// <summary>
	/// base class for all living entities
	/// </summary>
	public abstract class Creature : Tile
	{

		#region variables
		/// <summary>
		/// holds all the creature data from the data library 
		/// </summary>
		private CreatureData creatureData = new CreatureData();
		
		/// <summary>
		/// Event called when creature dies. Added a delegate so we don't need a null check.
		/// </summary>
		public event EventHandler Died = delegate{};
		/// <summary>
		/// event for movement
		/// </summary>
		public event EventHandler Moved = delegate{};

		/// <summary>
		/// Contains a list of potential moves.
		/// </summary>
		public Dictionary<Direction, int> Moves = new Dictionary<Direction, int>();

		private ActionManager AM = GameManager.Instance.ActionManager;

		#endregion

		#region constructor

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="imageCharacter"></param>
		/// <param name="color"></param>
		/// <param name="position"></param>
		public Creature(string imageCharacter, ConsoleColor color, Point position)
			: base(imageCharacter, color, position)
		{
			Setup();
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="position"></param>
		/// <param name="tileData"></param>
		public Creature(Point position, TileData tileData)
			: base(position, tileData)
		{
			Setup();
		}		
		
		/// <summary>
		/// Constructor
		/// </summary>
		public Creature()
		{ 
			Setup();
		}
		#endregion

		#region properties

		/// <summary>
		/// Test hits to check if creature is alive.
		/// </summary>
		public bool Alive
		{
			get { return (Hits >= 0); }
		}
		
		/// <summary>
		/// A list of custom bools
		/// </summary>
		public Dictionary<string,bool> Flags
		{
			get { return (this.creatureData.Flags);}
		}
		
		/// <summary>
		/// Used to safely add flags to the dictionary
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public void AddFlag(string key, bool value)
		{
			if (this.creatureData.Flags.ContainsKey(key))
				this.creatureData.Flags[key] = value;
			else
				this.creatureData.Flags.Add(key,value);
		}
		
		
		#endregion

		#region methods

		/// <summary>
		/// hits property
		/// </summary>
		public int Hits
		{
			get
			{
				return creatureData.Hits;
			}
			set
			{
				creatureData.Hits = value;
				if (creatureData.Hits <= 0)
						Died(this, EventArgs.Empty);
			}
		}

		/// <summary>
		/// Setup creature
		/// </summary>
		public void Setup()
		{
			Hits = 1;
		}
		
		//Populates the Moves list with distance from target to figure out the best move.
		/// <summary>
		/// Goes through neighbors to find which one is closest to the target...I think.  
		/// Perhaps just calculates the data for the next step.
		/// </summary>
		/// <param name="target"></param>
		public void CalculateMoves(Point target)
		{
			Moves.Clear();
			Moves.Add(Direction.N, DungeonHelper.GetDistance(DungeonHelper.AddPoints(Position, AM.DirectionVectors["N"]), target));
			Moves.Add(Direction.NE, DungeonHelper.GetDistance(DungeonHelper.AddPoints(Position, AM.DirectionVectors["NE"]), target));
			Moves.Add(Direction.E, DungeonHelper.GetDistance(DungeonHelper.AddPoints(Position, AM.DirectionVectors["E"]), target));
			Moves.Add(Direction.SE, DungeonHelper.GetDistance(DungeonHelper.AddPoints(Position, AM.DirectionVectors["SE"]), target));
			Moves.Add(Direction.S, DungeonHelper.GetDistance(DungeonHelper.AddPoints(Position, AM.DirectionVectors["S"]), target));
			Moves.Add(Direction.SW, DungeonHelper.GetDistance(DungeonHelper.AddPoints(Position, AM.DirectionVectors["SW"]), target));
			Moves.Add(Direction.W, DungeonHelper.GetDistance(DungeonHelper.AddPoints(Position, AM.DirectionVectors["W"]), target));
			Moves.Add(Direction.NW, DungeonHelper.GetDistance(DungeonHelper.AddPoints(Position, AM.DirectionVectors["NW"]), target));
		}

		/// <summary>
		/// calls Event for moved
		/// </summary>
		/// <param name="sender"></param>
		public void OnMove(object sender)
		{
			Moved(sender, EventArgs.Empty);
		}

		#endregion

	}
}
